﻿package kliment.effect {
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.GradientType;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.filters.BlurFilter;
	import flash.geom.ColorTransform;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import kliment.display.draw.DrawStyle;
	import kliment.display.draw.shape.RectangleShape;
	import kliment.display.draw.style.GradientStyle;
	import kliment.geom.Geom;
	import kliment.utils._Sprite;
	
	/**
	 * ...
	 * @author	Kliment
	 * @version 1.4.13
	 * 
	 * class name: kliment.effect.Reflection
	 */
	public class Reflection {
		private var _target:DisplayObject;
		private var _distance:Number = 0;
		private var _ratio:Number = 1;
		private var _reflectionColors:Array = [0x000000, 0x000000];
		private var _reflectionAlphas:Array = [0, 1];
		private var _reflectionRatios:Array = [0, 0xFF];
		private var _lifeUpdate:Boolean = false;
		private var _targetBounds:Rectangle;
		private var _blurFilter:BlurFilter;
		
		private var _reflectionHolder:Sprite;
		private var _reflectionBodyBitmap:Bitmap;
		private var _bodyBMP:BitmapData;
		private var _reflectionBMP:BitmapData;
		private var _maskBMP:BitmapData;
		private var _drawRectangle:Rectangle;
		private var _aBodyRect:Rectangle;
		private var _aMatrix:Matrix;
		private var _aDrawStyle:DrawStyle;
		private var _aColorTransform:ColorTransform;
		private var _aBlendMode:String = 'normal';
		private var _colorDraw:Number = 0x000000;
		private var _alphaDraw:Boolean = true;
		private var _pixelSnapping:String = 'auto';
		private var _pointDraw:Point;
		private var _clipRect:Rectangle;
		
		public var smoothing:Boolean = true;
		
		private var _x:Number;
		private var _y:Number;
	
		public function Reflection(target:DisplayObject, lifeUpdate:Boolean = false, clipRect:Rectangle = null) {
			_target = target;
			_clipRect = clipRect;
			_init();
			distance = _distance;
			this.lifeUpdate = lifeUpdate;
		}
		
		private function _init():void {
			_aColorTransform = new ColorTransform();
			_pointDraw = new Point();
			_aDrawStyle = new DrawStyle(null, null, new GradientStyle(GradientType.LINEAR, _reflectionColors, _reflectionAlphas, _reflectionRatios));
			
			_reflectionHolder = new Sprite();
			_reflectionBodyBitmap = new Bitmap();
			_reflectionHolder.addChild(_reflectionBodyBitmap);
			_target.parent.addChildAt(_reflectionHolder, _target.parent.getChildIndex(_target) + 1);
			
			_config();
		}
		
		private function _config():void {
			_drawRectangle = _target.getBounds(_target.parent);
			_drawRectangle.width = Math.round(_drawRectangle.width);
			_drawRectangle.height = Math.round(_drawRectangle.height);
			_clipRect = _clipRect || _drawRectangle.clone();
			_aMatrix = new Matrix();
			var createMatrix:Matrix = new Matrix();
			createMatrix.createGradientBox(_clipRect.width, _clipRect.height, Geom.HALF_PI, 0, 0);
			_aDrawStyle.fillGradientStyle.colors = _reflectionColors;
			_aDrawStyle.fillGradientStyle.alphas = _reflectionAlphas;
			_aDrawStyle.fillGradientStyle.ratios = _reflectionRatios;
			_aDrawStyle.fillGradientStyle.ratios = _reflectionRatios;
			_aDrawStyle.fillGradientStyle.matrix = createMatrix;
			var maskShape:RectangleShape = new RectangleShape(new Rectangle(0, 0, _clipRect.width, _clipRect.height), _aDrawStyle);
			
			if (_maskBMP){
				_maskBMP.dispose();
			}
			_maskBMP = new BitmapData(maskShape.width, maskShape.height, true, _colorDraw);
			_maskBMP.draw(maskShape);
			
			_reflectionBodyBitmap.scaleY = -1;
			_reflectionBodyBitmap.y = _clipRect.height;
			
			if (_bodyBMP){
				_bodyBMP.dispose();
			}
			_bodyBMP = new BitmapData(_clipRect.width, _clipRect.height, _alphaDraw, _colorDraw);
			
			if (_reflectionBMP){
				_reflectionBMP.dispose();
			}
			_reflectionBMP = new BitmapData(_clipRect.width, _clipRect.height, _alphaDraw, _colorDraw);
			_reflectionBodyBitmap.bitmapData = _reflectionBMP;
			_render();
		}
		
		
		private function _render(event:Event = null):void {
			_aBodyRect = _bodyBMP.rect;
			_bodyBMP.fillRect(_aBodyRect, _colorDraw);
			_bodyBMP.draw(_target, _aMatrix, _aColorTransform, _aBlendMode, null, smoothing);
			_reflectionBMP.fillRect(_aBodyRect, _colorDraw);
			_reflectionBMP.copyPixels(_bodyBMP, _aBodyRect, _pointDraw, _maskBMP, _pointDraw, _alphaDraw);
			_setPosition();
		}
		
		private function _setPosition():void {
			if (isNaN(_x) || isNaN(_y))
				_targetBounds = _target.getBounds(_target.parent);
			_reflectionHolder.scaleY = _target.scaleY * _ratio;
			_reflectionHolder.scaleX = _target.scaleX;
			_reflectionHolder.x = isFinite(_x)?_x:_targetBounds.x;
			_reflectionHolder.y = (isFinite(_y)?_y:_targetBounds.bottom) + _distance;
		}
		
		public function update():void {
			_render();
		}
		
		public function get lifeUpdate():Boolean { return _lifeUpdate; }
		public function set lifeUpdate(value:Boolean):void 	{
			if (_lifeUpdate == value) return;
			if (value) 
				_reflectionHolder.addEventListener(Event.ENTER_FRAME, _render);
			else 
				_reflectionHolder.removeEventListener(Event.ENTER_FRAME, _render);
			_lifeUpdate = value;
		}
		
		public function get distance():Number { return _distance; }
		public function set distance(value:Number):void {
			_distance = value;
			_setPosition();
		}	
		
		public function get ratio():Number { return _ratio; }
		public function set ratio(value:Number):void 	{
			_ratio = value;
			_setPosition();
		}
		
		public function get reflectionAlphas():Array { return _reflectionAlphas; }
		public function set reflectionAlphas(value:Array):void {
			_reflectionAlphas = value;
			_config();
			_render();
		}
		
		public function get reflectionRatios():Array { return _reflectionRatios; }
		public function set reflectionRatios(value:Array):void {
			_reflectionRatios = value;
			_config();
			_render();
		}
		
		public function get blurFilter():BlurFilter { return _blurFilter; }
		public function set blurFilter(value:BlurFilter):void {
			_blurFilter = value;
			if (value) 
				_reflectionHolder.filters = [value];
			else 
				_reflectionHolder.filters = [];
		}
		
		public function get x():Number { return _x; }
		public function set x(value:Number):void {
			_x = value;
			_setPosition();
		}
		
		public function get y():Number { return _y; }
		public function set y(value:Number):void {
			_y = value;
			_setPosition();
		}
		
		public function get clipRect():Rectangle { return _clipRect; }
		public function set clipRect(value:Rectangle):void {
			_clipRect = value;
			_config();
		}
		
		public function remove():void {
			_bodyBMP.dispose();
			_reflectionBMP.dispose();
			lifeUpdate = false;
			_Sprite.remove(_reflectionHolder);
		}
		
	}

}